import { computed, defineComponent, nextTick, PropType, reactive, ref } from "vue";
import "./index.scss";
import {
  VisualEditorModleValue,
  VisualEditorConfig,
  createNewBlock,
  VisualEditorBlockData,
  VisualEditorMarkLines
} from "./visualEditor.utils";
import { useModel } from "@/utils/useModel";
import { VisualEditorBlock } from "./visualEditorBlock";
import { VisualEditorComponent } from "./visualEditor.utils";
import { useVisualCommand } from "./visualEditor.command";
import { createEvent } from "@/utils/event";
import { $$dialog } from '../dialog/dialog-service'
import { $$dropdown, DropdownOption } from '../dropdown/dropdown-service'
import { ElMessageBox } from 'element-plus'
export const VisualEditor = defineComponent({
  props: {
    modelValue: {
      type: Object as PropType<VisualEditorModleValue>,
      require: true
    },
    config: {
      type: Object as PropType<VisualEditorConfig>
    }
  },
  components: {
    VisualEditorBlock
  },
  emits: {
    "updata:modelValue": (val?: VisualEditorModleValue) => true
  },
  setup(props, ctx) {
    const containerRef = ref({} as HTMLDivElement);

    const dataModel = useModel(
      () => props.modelValue,
      val => ctx.emit("updata:modelValue", val)
    );

    const containerStyles = computed(() => ({
      width: `${props.modelValue?.container.width}px`,
      height: `${props.modelValue?.container.height}px`
    }));

    /** 将组件根据选中/未选中状态进行区分 */
    const focusData = computed(() => {
      const blocks = dataModel.value!.blocks || [];
      const focus: VisualEditorBlockData[] = [];
      const unfocus: VisualEditorBlockData[] = [];
      blocks.map(block => (block.focus ? focus : unfocus).push(block));
      return {
        unfocus,
        focus
      };
    });
    const dragstart = createEvent();
    const dragend = createEvent();

    const state = reactive({
      /** 当前最后一次选中的组件 */
      selectBlock: null as null | VisualEditorBlockData  
    })
    /** 拖拽事件 */
    const menuDraggier = (() => {
      //当前拖拽的组件
      const current = {
        component: null as null | VisualEditorComponent
      };
      //拖拽元素
      const blockHandler = {
        //拖拽元素进入目标元素
        dragenter: (e: DragEvent) => {
          e.dataTransfer!.dropEffect = "move";
        },
        //拖拽元素离开目标元素
        dragleave: (e: DragEvent) => {
          e.dataTransfer!.dropEffect = "none";
        },
        //拖拽元素在目标元素上移动
        dragover: (e: DragEvent) => {
          e.preventDefault();
        },
        //拖拽元素在目标元素上松开鼠标
        drop: (e: DragEvent) => {
          const blocks = dataModel.value?.blocks || [];
          blocks.push(
            createNewBlock({
              top: e.offsetY,
              left: e.offsetX,
              component: current.component!
            })
          );
          /** 发布，通知drag命令执行 */
          dragend.emit();
        }
      };
      //目标元素
      const containerHandler = {
        //开始拖拽
        dragstart: (e: DragEvent, component: VisualEditorComponent) => {
          containerRef.value.addEventListener(
            "dragenter",
            blockHandler.dragenter
          );
          containerRef.value.addEventListener(
            "dragleave",
            blockHandler.dragleave
          );
          containerRef.value.addEventListener(
            "dragover",
            blockHandler.dragover
          );
          containerRef.value.addEventListener("drop", blockHandler.drop);
          current.component = component;
          /** 发布，通知drag命令执行 */
          dragstart.emit();
        },
        //拖拽结束
        dragend: () => {
          containerRef.value.removeEventListener(
            "dragenter",
            blockHandler.dragenter
          );
          containerRef.value.removeEventListener(
            "dragleave",
            blockHandler.dragleave
          );
          containerRef.value.removeEventListener(
            "dragover",
            blockHandler.dragover
          );
          containerRef.value.removeEventListener("drop", blockHandler.drop);
          current.component = null;
        }
      };
      return containerHandler;
    })();

    /** 封装的方法 */
    const methods = {
      /**
       * 清除选中状态
       * 传入参数则清除除其之外组件的选中状态
       */
      clearFocus: (block?: VisualEditorBlockData) => {
        let blocks = dataModel.value?.blocks || [];
        blocks = blocks.filter(v => v.uid !== block?.uid);
        // console.log('blocks',blocks);
        blocks.forEach(block => {
          block.focus = false;
        });
      },
      /** 更新画布中组件 */
      updataBlocks: (blocks: VisualEditorBlockData[]) => {
        dataModel.value = {
          ...dataModel.value!,
          blocks
        };
      }
    };

    /** 选中事件，使用mousedown是为了在按下时就显示选中状态，click事件选中按下并松开才会触发 */
    const focusHandler = (() => {
      return {
        /** 点击画布元素 */
        container: {
          onMousedown: (e: MouseEvent) => {
            /** 只处理容器/画布的点击事件，如果点击组件冒泡触发容器事件，这里是不处理的 */
            if(e.currentTarget !== e.target)return
            // e.stopPropagation();
            // e.preventDefault();
            if(!e.shiftKey){
              /** 点击容器空白处，清空组件选中状态 */
              methods.clearFocus();
              state.selectBlock = null
            }
          }
        },
        /** 点击组件元素 */
        block: {
          onMousedown: (
            e: MouseEvent,
            block: VisualEditorBlockData,
            selectIndex?: number
          ) => {
            /** elementUi使用的事件捕获，这里阻止事件冒泡事件会导致控制台报错 */
            // e.stopPropagation();
            // e.preventDefault();
            if (e.shiftKey) {
              /** shift + 点击 */
              block.focus = !block.focus;
              /** 如果组件选中，则赋值，如果取消选中状态，则不做任何记录，继续使用上一次选中的组件 */
              if(block.focus){
                state.selectBlock = block
              }
            } else {
              /** 如果组件没有选中，则设为选中状态，如果已经被选中，再次点击，则有可能为移动 */
              if (!block.focus) {
                block.focus = true;
                methods.clearFocus(block);
              }
              /** shift健没有同时按下，记录当前的选中组件 */
              state.selectBlock = block
            }
            blockDraggier.mouseDown(e);
          }
        }
      };
    })();

    /** 组件的移动 */
    const blockDraggier = (() => {
      let dragStart = {
        startX: 0,
        startY: 0,
        dragging: false,
        focsuPosition: [] as { left: number; top: number }[],
        markLines: {} as VisualEditorMarkLines
      };
      const markLine = reactive({
        x: null as null | number,
        y: null as null | number,
      })
      /** 鼠标按下 */
      const mouseDown = (e: MouseEvent) => {
        dragStart = {
          startX: e.clientX,
          startY: e.clientY,
          dragging: false,
          focsuPosition: focusData.value.focus.map(({ left, top }) => ({
            left,
            top
          })),
          markLines: (()=>{
            const lines: VisualEditorMarkLines = {x: [], y: []}
            const { unfocus } = focusData.value
            const {top, left, width, height} = state.selectBlock!
            unfocus.forEach(block => {
              const {left: l, top: t, width: w, height: h} = block
              /** 记录未选中组件和选中组件贴边对齐时的top位置和边线显示需要的位置 */
              lines.y.push({top:t, showTop: t})                               //顶部对顶部
              lines.y.push({top: t + h, showTop: t + h})                      //顶部对底部
              lines.y.push({top: t + h/2 - height/2, showTop: t + h/2})       //中间对中间
              lines.y.push({top: t - height, showTop: t  })                   //底部对顶部
              lines.y.push({top: t + h - height, showTop: t + h })            //底部对底部

              lines.x.push({left:l, showLeft: l})                               //左边对左边
              lines.x.push({left: l + w, showLeft: l + w})                      //右边对左边
              lines.x.push({left: l + w/2 - width/2, showLeft: l + w/2})        //中间对中间
              lines.x.push({left: l - width, showLeft: l })                     //右边对左边
              lines.x.push({left: l + w - width, showLeft: l + w })             //右边对右边
            })
            
            return lines
          })()
        };
        window.addEventListener("mousemove", mousemove);
        window.addEventListener("mouseup", mouseup);
      };
      /** 鼠标移动 */
      const mousemove = (e: MouseEvent) => {
        /** 组件移动时派发事件，通知状态的收集，方便撤销和重做功能的实现 */
        if (!dragStart.dragging) {
          dragStart.dragging = true;
          dragstart.emit();
        }

        const { clientX, clientY } = e
        /** 鼠标移动的距离 */
        let durX = clientX - dragStart.startX;
        let durY = clientY - dragStart.startY;

        if(e.shiftKey){
          /** shift健下组件水平垂直移动 */
          if(Math.abs(durX) > Math.abs(durY)){
            durY = 0
          }else {
            durX = 0
          }
        }
        /** 让选中的组件移动 */
        focusData.value.focus.forEach((block, index) => {
          block.left = dragStart.focsuPosition[index].left + durX;
          block.top = dragStart.focsuPosition[index].top + durY;
        });
        /** 设置贴边线的坐标 */
        const {left: moveX, top: moveY} = state.selectBlock!
        for (let index = 0; index < dragStart.markLines.x.length; index++) {
          const {left, showLeft} = dragStart.markLines.x[index];
          if(Math.abs(moveX - left) < 5){
            markLine.x = showLeft
            state.selectBlock!.left = left
            break;
          }else{
            markLine.x = null
          }
        }
        for (let index = 0; index < dragStart.markLines.y.length; index++) {
          const {top, showTop} = dragStart.markLines.y[index];
          if(Math.abs(moveY - top) < 5){
            markLine.y = showTop
            state.selectBlock!.top = top
            break;
          }else{
            markLine.y = null
          }
        }
      };
      /** 鼠标松开 */
      const mouseup = () => {
        window.removeEventListener("mousemove", mousemove);
        window.removeEventListener("mouseup", mouseup);
        if (dragStart.dragging) {
          dragend.emit();
        }
        /** 鼠标松开，贴边线消失 */
        markLine.x = null
        markLine.y = null
      };
      return { 
        mouseDown,
        markLine
       };
    })();

    /** 其他事件 */
    const handler = (() => {
      /** 鼠标右击事件 */
      const onContextMenu = (e: MouseEvent, block: VisualEditorBlockData) => {
        e.stopPropagation();
        e.preventDefault();

        $$dropdown({
          reference: e,
          render:() => (
            <>
             <DropdownOption label="置顶节点" icon="icon-place-top" {...{onClick: commander.placedTop}}/>
             <DropdownOption label="置底节点" icon="icon-place-bottom" {...{onClick: commander.placedBottom}}/>
             <DropdownOption label="删除节点" icon="icon-delete" {...{onClick: commander.delete}}/>
             <DropdownOption label="导入节点" icon="icon-import" {...{onClick: commander.importBlock}}/>
             <DropdownOption label="导出节点" icon="icon-export" {...{onClick: () => $$dialog.textarea(JSON.stringify(state.selectBlock), '导出的JSON数据', {editReadonly: true })}}/>
            </>
          )
        })
      }
      return {
        onContextMenu
      }
    })()

    const commander = useVisualCommand({
      focusData: focusData,
      dataModel: dataModel,
      updataBlocks: methods.updataBlocks,
      dragStart: dragstart,
      dragEnd: dragend
    });
    const buttons = [
      {
        label: "撤销",
        icon: "icon-back",
        event: commander.undo,
        tip: "ctrl+z"
      },
      {
        label: "重做",
        icon: "icon-forward",
        event: commander.redo,
        tip: "ctrl+y, ctrl+shift+z"
      },
      {
        label: '导入', icon: 'icon-import', event: commander.import,
    },
    {
        label: '导出',
        icon: 'icon-export',
        event: () => $$dialog.textarea(JSON.stringify(dataModel.value), '导出的JSON数据', {editReadonly: true})
    },
    {
        label: '置顶',
        icon: 'icon-place-top',
        event: commander.placedTop
    },
    {
        label: '置底',
        icon: 'icon-place-bottom',
        event: commander.placedBottom
    },
      {
        label: "删除",
        icon: "icon-delete",
        event: () => commander.delete(),
        tip: "ctrl+d, backspace, delete"
      },
      { label: "清空", icon: "icon-reset", event: commander.clear, tip: "" }
    ];

    return () => (
      <div class="visual-editor">
        {/* 左侧列表 */}
        <div class="visual-editor-menu">
          {props.config?.componentList.map((component, index) => (
            <div
              draggable
              class="visual-editor-menu-item"
              key={index}
              onDragstart={e => menuDraggier.dragstart(e, component)}
              onDragend={menuDraggier.dragend}
            >
              <span class="visual-editor-menu-item-label">
                {component.label}
              </span>
              {component.preview}
            </div>
          ))}
        </div>
        {/* 操作栏 */}
        <div class="visual-editor-head">
          {buttons.map((button, index) => {
            const content = (
              <div class="visual-editor-head-item" onClick={button.event}>
                <i class={`iconfont ${button.icon}`}></i>
                <span class="visual-editor-head-item-label">
                  {button.label}
                </span>
              </div>
            );

            return !button.tip ? (
              content
            ) : (
              <el-tooltip effect="dark" content={button.tip} placement="bottom">
                {content}
              </el-tooltip>
            );
          })}
        </div>
        <div class="visual-editor-operator">visual-editor-operator</div>
        {/* 中间画布 */}
        <div class="visual-editor-body">
          <div class="visual-editor-content">
            <div
              class="visual-editor-container"
              style={containerStyles.value}
              ref={containerRef}
              {...focusHandler.container}
            >
              {!!dataModel.value &&
                !!dataModel.value.blocks &&
                dataModel.value.blocks.map((block, index) => (
                  <VisualEditorBlock
                    block={block}
                    config={props.config}
                    key={index}
                    {...{
                      onMousedown: (e: MouseEvent) =>
                        focusHandler.block.onMousedown(e, block, index),
                      onContextMenu: (e: MouseEvent) => handler.onContextMenu(e, block)
                    }}
                  />
                ))}
                { blockDraggier.markLine.x !== null && ( <div class="visual-editor-container-line-x" style={{left: `${blockDraggier.markLine.x}px`}}></div>)}
                { blockDraggier.markLine.y !== null && (<div class="visual-editor-container-line-y" style={{top: `${blockDraggier.markLine.y}px`}}></div>)}
            </div>
          </div>
        </div>
      </div>
    );
  }
});
